package openase.audiosearch.comm;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class CommUtil {

	private CommUtil() {
	}

	public static SocketChannel connect(String ip, int port) throws IOException {
		SocketChannel channel = SocketChannel.open();
		// instantly connected
		if (channel.connect(new InetSocketAddress(ip, port))) {
			return channel;
		}

		final long ini = System.currentTimeMillis();
		final long timeout = 30000;
		while (!channel.finishConnect()) {
			if (System.currentTimeMillis() - ini > timeout) {
				try {
					channel.close();
				} catch (IOException ioe) {
					// ignore.
				}
				throw new SocketTimeoutException("Connection establishment exceeded the " + timeout + "ms timeout.");
			}
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// ignore...
			}
		}
		return channel;// connected!
	}

	public static ByteBuffer recv(SocketChannel channel) throws IOException {
		ByteBuffer sizeBuffer = ByteBuffer.allocate(4);
		int read;

		while (sizeBuffer.hasRemaining()) {
			read = channel.read(sizeBuffer);
			if (read < 0) {
				throw new IOException(String.format("Expected to read %d bytes, but read %d.", sizeBuffer.limit(), sizeBuffer.position()));
			}
			sleep(50);
		}
		sizeBuffer.flip();
		int bufSize = sizeBuffer.getInt();

		// TODO add timeout
		ByteBuffer inputBuffer = ByteBuffer.allocate(bufSize);
		while (inputBuffer.hasRemaining()) {
			read = channel.read(inputBuffer);
			if (read < 0) {
				throw new IOException("Communication seems closed.");
			}
			sleep(50);
		}

		inputBuffer.flip();
		return inputBuffer;
	}

	public static void send(SocketChannel channel, ByteBuffer outputBuffer) throws IOException {
		outputBuffer.flip();

		ByteBuffer sizeBuffer = ByteBuffer.allocate(4);
		sizeBuffer.putInt(outputBuffer.limit());
		sizeBuffer.flip();
		ByteBuffer[] buffers = { sizeBuffer, outputBuffer };

		// TODO add timeout
		while (sizeBuffer.hasRemaining() || outputBuffer.hasRemaining()) {
			channel.write(buffers);
		}
	}

	private static void sleep(int timeInMillis) {
		try {
			Thread.sleep(timeInMillis);
		} catch (InterruptedException e) {
			// ignore
		}
	}
}
